package il.ac.tau.cvws.scrabblesidekick.client.engine;

import il.ac.tau.cvws.scrabblesidekick.client.app.controller.CameraController;
import il.ac.tau.cvws.scrabblesidekick.shared.util.ColorUtil;
import il.ac.tau.cvws.scrabblesidekick.shared.util.math.Point;
import android.graphics.Bitmap;

/* The base camera engine. The basic functionality of this engine (which
 * is augmented by its subclasses) is taking a preview image and determining
 * whether or not the camera is positioned correctly for a high-quality
 * picture to be taken.
 */
public abstract class CameraEngine
{
	private static final double BORDER_THRESHOLD = 0.9;
	
	protected final CameraController _controller;
	
	public CameraEngine(CameraController controller)
	{
		_controller = controller;
	}
	
	/* The main functionality of the engine is located in this method. This method
	 * takes a preview image and a border (actually 4 points of a rectangle), and
	 * determines whether or not the board in the image is located at the given border,
	 * up to a predetermined tolerance. If it is, we cue the camera activity to snap
	 * a high-quality picture to be sent to the server for processing.
	 */
	public boolean isPlacedOnBoard(Bitmap image, Point[] border)
	{
		/* The top and bottom edges of the border are said to be positioned correctly
		 * if at least a set predetermined percentage (e.g. 90%) of them are laid
		 * on the border.
		 */
		int topWidthThreshold		= (int)(BORDER_THRESHOLD * (border[1].x - border[0].x));
		int bottomWidthThreshold 	= (int)(BORDER_THRESHOLD * (border[2].x - border[3].x));
		
		boolean foundSide = false;
		int matchCount = 0;
		
		for (int x = (int)border[0].x; x < (int)border[1].x; x++)
		{
			if (isBorder(image, x, (int)border[0].y, 2))
			{
				matchCount++;
				
				if (matchCount > topWidthThreshold)
				{
					foundSide = true;
				}
			}
		}
		
		if (!foundSide)
		{
			return false;
		}
		
		foundSide = false;
		matchCount = 0;
		
		for (int x = (int)border[3].x; x < (int)border[2].x; x++)
		{
			if (isBorder(image, x, (int)border[3].y, 2))
			{
				matchCount++;
				
				if (matchCount > bottomWidthThreshold)
				{
					foundSide = true;
				}
			}
		}
		
		if (!foundSide)
		{
			return false;
		}
		
		return true;
	}
	
	/* Determines whether or not a point on a given image is black, taking
	 * into consideration a given epsilon value. The point is considered
	 * black if any of the points surrounding it up to the epsilon are black.
	 */
	private boolean isBorder(Bitmap image, int x, int y, int epsilon)
	{	
		for (int i = -epsilon; i < epsilon; i++)
		{
			for (int j = -epsilon; j < epsilon; j++)
			{
				int nx = x + i;
				int ny = y + j;
				
				if ((nx > 0) && (nx < image.getWidth() - 1) &&
					(ny > 0) && (ny < image.getHeight() - 1))
				{
					int col = image.getPixel(nx, ny);
					
					if (ColorUtil.isBlack(col))
					{
						return true;
					}	
				}
			}
		}
		
		return false;
	}
}
